/*
 * Copyright (C) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "aki/jsbind.h"
namespace {

aki::Promise CreatePromiseRejectAndCatch()
{
    aki::Promise promise;
    const int notFound = 404;
    promise.Reject(notFound);
    
    std::function<int(int)> errorFunc = [](int errorCode)->int {
        if (errorCode == 404) {
            const int ok = 200;
            return ok;
        } else {
            return -1;
        }
    };
    
    return promise.Catch(errorFunc);
}

aki::Promise CreatePromiseRejectAndThen()
{
    aki::Promise promise;
    const int notFound = 404;
    promise.Reject(notFound);
    
    std::function<int(int)> func = [](int num)->int {
        const int ok = 200;
        return ok;
    };
    
    return promise.Then(func);
}

aki::Promise CreatePromiseResolveAndThen()
{
    aki::Promise promise;
    const int success = 666;
    promise.Resolve(success);
    
    std::function<int(int)> func = [](int num)->int {
        if (num == 666) {
            const int ok = 200;
            return ok;
        } else {
            return -1;
        }
    };
    
    return promise.Then(func);
}

aki::Promise CreatePromiseResolveAndCatch()
{
    aki::Promise promise;
    const int success = 666;
    promise.Resolve(success);
    
    std::function<int(int)> errorFunc = [](int errorCode)->int {
        const int ok = 200;
        return ok;
    };
    
    return promise.Catch(errorFunc);
}

aki::Promise CreatePromiseLongChain()
{
    aki::Promise promise;
    promise.Resolve(1);
    
    std::function<int(int)> func = [](int num)->int {
        return num+1;
    };
    
    return promise.Then<int, int>([](int num)->int {
        const int testNumber = 9;
        return num+testNumber;
    }).Then(func).Then(func).Then(func).Then(func).Then(func).Then(func);
}

aki::Promise CreatePromiseLongChainReject()
{
    aki::Promise promise;
    promise.Reject(1);
    
    std::function<int(int)> func = [](int num)->int {
        return num+1;
    };
    
    return promise.Then<int, int>([](int num)->int {
        const int testNumber = 9;
        return num+testNumber;
    }).Then(func).Then(func).Then(func).Then(func).Then(func).Then(func);
}

aki::Promise CreatePromiseThenCatchResolve()
{
    aki::Promise promise;
    promise.Resolve("task finished!");

    return promise.Then<std::string, std::string, std::string, std::string>([](std::string info)->std::string {
        return "then function finished";
        }, [](std::string error) {
        return "catch function finished";
    });
}

aki::Promise CreatePromiseThenCatchReject()
{
    aki::Promise promise;
    promise.Reject("task failed!");

    return promise.Then<std::string, std::string, std::string, std::string>([](std::string info)->std::string {
        return "then function finished";
        }, [](std::string error)-> std::string {
        return "catch function finished";
    });
}

aki::Promise ProcessJsPromise(aki::Promise promise)
{
    return promise.Then<std::string, std::string>([](std::string info)->std::string {
        return info + " (processed by C then)";
    }).Catch<std::string, std::string>([](std::string info)->std::string {
        return info + " (processed by C catch)";
    });
}

aki::Promise ProcessJsPromiseError(aki::Promise promise)
{
    return promise.Catch<int, aki::Value>([](aki::Value error)->int {
        if (error.IsError()) {
            const int ok = 200;
            return ok;
        } else {
            return -1;
        }
    });
}

aki::Promise ProcessJsPromiseCatchThrowAgain(aki::Promise promise)
{
    return promise.Catch<int, aki::Value>([](aki::Value error)->int {
        if (error.IsError()) {
            const int ok = 200;
            return ok;
        } else {
            return -1;
        }
    }).Then<void, int>([](int num) {
        const int ok = 200;
        if (num == ok) {
            napi_throw_error(aki::Binding::GetScopedEnv(), "500", "throw exception test");
        }
    }).Catch<int, aki::Value>([](aki::Value error)->int {
        if (error.IsError()) {
            if (error["message"].As<std::string>() == "throw exception test") {
                const int processed = 666;
                return processed;
            }
        }

        return -1;
    });
}

} // end of namespace

JSBIND_GLOBAL()
{
    JSBIND_FUNCTION(CreatePromiseRejectAndCatch);
    JSBIND_FUNCTION(CreatePromiseRejectAndThen);
    JSBIND_FUNCTION(CreatePromiseResolveAndThen);
    JSBIND_FUNCTION(CreatePromiseResolveAndCatch);
    JSBIND_FUNCTION(CreatePromiseLongChain);
    JSBIND_FUNCTION(CreatePromiseLongChainReject);
    JSBIND_FUNCTION(CreatePromiseThenCatchResolve);
    JSBIND_FUNCTION(CreatePromiseThenCatchReject);
    JSBIND_FUNCTION(ProcessJsPromise);
    JSBIND_FUNCTION(ProcessJsPromiseError);
    JSBIND_FUNCTION(ProcessJsPromiseCatchThrowAgain);
}